package com.jintoufs.web.action.refund;


import com.jintoufs.domain.common.WxCommon;
import com.jintoufs.domain.constants.Constants;
import com.jintoufs.domain.constants.WxConstant;

import com.jintoufs.domain.hotel.HotelOrder;
import com.jintoufs.domain.scene.SceneOrder;
import com.jintoufs.domain.sys.Dictionary;
import com.jintoufs.domain.tour.TourRouteOrder;
import com.jintoufs.domain.wxpay.Refund;
import com.jintoufs.domain.wxpay.RefundBack;
import com.jintoufs.service.hotel.HotelOrderService;
import com.jintoufs.service.scene.SceneOrderService;
import com.jintoufs.service.sys.DictionaryService;
import com.jintoufs.service.tour.TourRouteOrderService;
import com.jintoufs.service.weixin.WxRefundService;
import com.jintoufs.util.valid.ValidUtil;
import com.jintoufs.util.weixin.RandomUtils;
import com.jintoufs.util.weixin.AesCbcUtil;
import com.jintoufs.web.action.support.SupportAction;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import org.cosmos.modules.web.msg.AjaxMsg;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLDecoder;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("refund")
public class RefundAction extends SupportAction {

    private static Logger log = Logger.getLogger(RefundAction.class);
    @Resource
    private WxRefundService refundService;
    @Resource
    private HotelOrderService hotelOrderService;
    @Resource
    private SceneOrderService sceneOrderService;
    @Resource
    private TourRouteOrderService tourRouteOrderService;
    @Resource
    private DictionaryService dictionaryService;
    private static final String MCH_ID = "1496855682";


    private Refund createRefundInfo(String orderId, String refundId, String refundDescribe,Integer price) {

        Refund refund = new Refund();
//        refund.setId(refundId);
        refund.setAppid(WxConstant.APP_ID);
        refund.setMch_id(WxConstant.MCH_ID);
        refund.setNonce_str(RandomUtils.generateMixString(32));
        refund.setOut_trade_no(orderId);
        refund.setOut_refund_no(refundId);
        refund.setRefund_fee(price);
        refund.setSign_type("MD5");
        refund.setRefund_fee_type("CNY");
        refund.setRefund_desc(refundDescribe);
        //单位为分
        refund.setTotal_fee(price);
        refund.setNotify_url(WxConstant.URL_NOTIFY_REFUND);
        return refund;
    }

    private String getSign(Refund refund) throws Exception {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("appid=").append(refund.getAppid())
                .append("&mch_id=").append(refund.getMch_id())
                .append("&nonce_str=").append(refund.getNonce_str())
                .append("&out_refund_no=").append(refund.getOut_refund_no())
                .append("&out_trade_no=").append(refund.getOut_trade_no())
//                .append("&refund_desc=").append(refund.getRefund_desc())
                .append("&refund_fee=").append(refund.getRefund_fee())
                .append("&refund_fee_type=").append(refund.getRefund_fee_type())
                .append("&sign_type=").append(refund.getSign_type())
                .append("&total_fee=").append(refund.getTotal_fee())
                .append("&key=" + WxConstant.APP_KEY);

        System.out.println("encrypt refund args:" + stringBuilder.toString());

        return WxCommon.getMD5(stringBuilder.toString().trim()).toUpperCase();
    }


    /**
     * 退款接口
     *
     * @param orderId        订单ID
     * @param refundDescribe 退款描述
     * @return 退款单号
     */
    @RequestMapping("doRefund")
    @ResponseBody
    public AjaxMsg refundOrder(String orderId, String refundDescribe) {
        AjaxMsg ajaxMsg = new AjaxMsg();
        HotelOrder hotelOrder = null;
        SceneOrder sceneOrder = null;
        TourRouteOrder routeOrder = null;
        Map<String,Object> params = new HashMap<>();
        params.put("id",orderId);
        List<Map<String,Object>> orderList=null;
        Integer smsId;
        ArrayList<String> smsParams = new ArrayList<String>();
        hotelOrder = hotelOrderService.getById(orderId);
        smsId = Integer.valueOf(dictionaryService.getDicByKeyNo("ALL_ORDER_FAIL").get(0).getCaption());
        if (!ValidUtil.isEmpty(hotelOrder)){
            orderList = hotelOrderService.selectByParam(params);
            Object object;
            object = hotelOrder.getHotelId().substring(hotelOrder.getId().length()-5,hotelOrder.getId().length()-1);
            if (ValidUtil.isEmpty(object)){
                object="";
            }
            smsParams.add(object.toString());

            if (ValidUtil.isEmpty(object)){
                object="";
            }
            smsParams.add("酒店");
        }

        sceneOrder = sceneOrderService.getById(orderId);
        if (!ValidUtil.isEmpty(sceneOrder)){
            orderList = sceneOrderService.getByParam(params);
            smsParams.clear();
            Object object;
            object = sceneOrder.getSceneId().substring(sceneOrder.getId().length()-5,sceneOrder.getId().length()-1);
            if (ValidUtil.isEmpty(object)){
                object="";
            }
            smsParams.add(object.toString());

            if (ValidUtil.isEmpty(object)){
                object="";
            }
            smsParams.add("鱼·定制");
            }
        routeOrder = tourRouteOrderService.getById(orderId);
        if (!ValidUtil.isEmpty(routeOrder)){
            orderList = tourRouteOrderService.getOrderList(params);

            smsParams.clear();
            Object object;
            object = routeOrder.getAgencyId().substring(routeOrder.getId().length()-5,routeOrder.getId().length()-1);
            if (ValidUtil.isEmpty(object)){
                object="";
            }
            smsParams.add(object.toString());

            if (ValidUtil.isEmpty(object)){
                object="";
            }
            smsParams.add("线路");
        }

        if (!ValidUtil.isEmpty(orderList)&&!ValidUtil.isEmpty(orderList.get(0).get("price"))) {
            try {
                String url = WxConstant.URL_REFUND_ORDER;
                String refundId = getUUID();
                refundDescribe = "";
                Integer price = (new BigDecimal(orderList.get(0).get("price").toString()).multiply(new BigDecimal("100"))).intValue();
                Refund refund = createRefundInfo(orderId, refundId, refundDescribe,price);
                refund.setNotify_url("");
                //签名
                String md5 = getSign(refund);
                refund.setSign(md5);

                String xml = WxCommon.refund2XML(refund);
                xml = xml.replace("__", "_");

                log.debug("refund request xml: \n " + xml);
                String rawResult = httpsRefund(xml);
                log.debug("refund request return body: \n" + rawResult);
                Map<String, String> result = WxCommon.parseXml(rawResult);

                String returnCode = result.get("return_code");
                String returnMsg = result.get("return_msg");

                if ("FAIL".equals(returnCode) || StringUtils.isNotBlank(returnMsg) && !returnMsg.equals("OK")) {
                    throw new Exception(returnMsg);
                }
                String resultCode = result.get("result_code");
                if ("FAIL".equals(resultCode)) {
                    throw new Exception(result.get("err_code_des"));
                }
                refund.setTransaction_id(result.get("transaction_id"));
                refund.setId(refundId);
                refundService.insertRefund(refund);
                if (!ValidUtil.isEmpty(hotelOrder)){
                    hotelOrder.setStatus(Constants.ORDER_STATUS_CANCEL);
                    hotelOrderService.updateOrder(hotelOrder);
                }
                if (!ValidUtil.isEmpty(sceneOrder)){
                    sceneOrder.setStatus(Constants.ORDER_STATUS_CANCEL);
                    sceneOrderService.updateOrder(sceneOrder);
                }
                if (!ValidUtil.isEmpty(routeOrder)){
                    routeOrder.setStatus(Constants.ORDER_STATUS_CANCEL);
                    tourRouteOrderService.update(routeOrder);
                }
            }catch (Exception  e){
                e.printStackTrace();
                ajaxMsg.setCode(AjaxMsg.FAILURE);
                ajaxMsg.setMessage("退款时发生错误，请联系平台解决。");
                return ajaxMsg;
            }
            //发送消息

//            try {
//                String telephone = orderList.get(0).get("telephone").toString();
//                refundService.sendOrderSms(telephone,smsId,smsParams);
//            }catch (Exception e){
//                e.printStackTrace();
//                ajaxMsg.setCode(AjaxMsg.FAILURE);
//                ajaxMsg.setMessage("发送短信时发生错误，请联系平台解决。");
//            }
        }else {
            ajaxMsg.setCode(AjaxMsg.FAILURE);
            ajaxMsg.setMessage("订单数据异常，请联系平台解决");
        }

        return ajaxMsg;
    }


    public static String setXml(String return_code, String return_msg) {
        return "<xml><return_code><![CDATA[" + return_code + "]]>" +
                "</return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
    }

    @PostMapping("/notify")
    @ResponseBody
    public void notify(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        String inputLine;
        StringBuilder notifyXml = new StringBuilder();
        String result = setXml("SUCCESS", "OK");//返回给微信的处理结果

        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        response.setHeader("Access-Control-Allow-Origin", "*");
        //微信给返回的东西
        while ((inputLine = request.getReader().readLine()) != null) {
            notifyXml.append(inputLine);
        }
        request.getReader().close();

        if (StringUtils.isEmpty(notifyXml.toString())) {
            result = setXml("FAIL", "xml为空");
        }
        // parse step 1
        Map<String, String> reqRAW = WxCommon.parseXml(notifyXml.toString());
        try {
            refundBack(reqRAW);
        } catch (Exception e) {
            result = setXml("FAIL", e.getMessage());
            throw e;
        } finally {
            response.getWriter().write(result);
            response.getWriter().flush();
            log.debug("返回给微信的xml：" + result);
        }
    }

    public void refundBack(Map<String, String> reqRAW) throws Exception {
        synchronized (this) {
            if ("FAIL".equals(reqRAW.get("return_code"))) {
                throw new Exception(reqRAW.get("return_msg"));
            }
            RefundBack refundBack = new RefundBack();
            // parse step 2
            String reqInfo = reqRAW.get("req_info");
            Map<String, String> decryptedInfo = WxCommon.parseXml(AesCbcUtil.aes256Decode(reqInfo, WxCommon.getMD5(WxConstant.APP_KEY)));
            refundBack.setAppid(reqRAW.get("appid"));
            refundBack.setMch_id(reqRAW.get("mch_id"));
            refundBack.setTransaction_id(decryptedInfo.get("transaction_id"));
            refundBack.setOut_trade_no(decryptedInfo.get("out_trade_no"));
            refundBack.setRefund_id(decryptedInfo.get("refund_id"));
            refundBack.setOut_refund_no(decryptedInfo.get("out_refund_no"));
            refundBack.setTotal_fee(decryptedInfo.get("total_fee"));
            refundBack.setSettlement_total_fee(decryptedInfo.get("settlement_total_fee"));
            refundBack.setRefund_fee(decryptedInfo.get("refund_fee"));
            refundBack.setSettlement_refund_fee(decryptedInfo.get("settlement_refund_fee"));
            refundBack.setRefund_status(decryptedInfo.get("refund_status"));
            refundBack.setSuccess_time(decryptedInfo.get("success_time"));
            refundBack.setRefund_recv_accout(decryptedInfo.get("refund_recv_accout"));
            refundBack.setRefund_account(decryptedInfo.get("refund_account"));
            refundBack.setRefund_request_source(decryptedInfo.get("refund_request_source"));
            // hotel_order refund
            hotelOrderService.refundConfirm(refundBack.getOut_trade_no());
//            if (StringUtils.isEmpty(hotelOrderService.refundConfirm(refundBack.getOut_trade_no()))) {
//                throw new Exception("退款失败：未找到" + refundBack.getOut_refund_no() + "对应的orderPrice");
//            }
            refundService.insertRefundBack(refundBack);
        }
    }


    private String httpsRefund(String body) throws Exception {
        System.out.println(body);
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        URL url = this.getClass().getClassLoader().getResource("apiclient_cert.p12");
        assert url != null;
        File file = new File(URLDecoder.decode(url.getFile(), "UTF-8"));
        System.out.println(file.getAbsolutePath());
        FileInputStream instream = new FileInputStream(file);
        StringBuilder result = new StringBuilder();
        try {
            keyStore.load(instream, MCH_ID.toCharArray());
        }catch (Exception e){
            e.printStackTrace();
        }
        finally {
            instream.close();
        }
        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, MCH_ID.toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[]{"TLSv1"},
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();
        try {

            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
            httpPost.setEntity(new StringEntity(body, "UTF-8"));
            System.out.println("executing request " + httpPost.getRequestLine());

            CloseableHttpResponse response = httpclient.execute(httpPost);
            try {
                HttpEntity entity = response.getEntity();

                System.out.println("----------------------------------------");
                System.out.println(response.getStatusLine());
                if (entity != null) {
                    System.out.println("Response content length: " + entity.getContentLength());
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"utf-8"));
                    String text;
                    while ((text = bufferedReader.readLine()) != null) {
                        result.append(text);
                        System.out.println(text);
                    }

                }
                EntityUtils.consume(entity);
            }catch (Exception e){
                e.printStackTrace();
            }
            finally {
                response.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        finally {
            httpclient.close();
        }
        return result.toString();
    }
}